#include "common.h"

#define BUFFER_SIZE 4096
#define MAX_FILENAME_LEN 255
#define MAX_FILEPATH_LEN 511
#define MAX_SAVE_DIR_LEN 255
#define MAX_FILE_SIZE (10ULL * 1024 * 1024 * 1024) // 10GB

/*******************************************************************
 简要描述：确保完整发送数据
 参数1：socket描述符
 参数2：数组指针
 参数3：数组长度
 返回值：成功返回0 失败返回-1
********************************************************************/
int send_all(int sockfd, const void *buf, size_t len) 
{
    size_t total_sent = 0;
    const char *ptr = (const char *)buf;
    
    while (total_sent < len) 
    {
        ssize_t sent = send(sockfd, ptr + total_sent, len - total_sent, 0);
        if (sent <= 0) 
        {
            return -1; // 发送错误
        }
        total_sent += sent;
    }
    
    return 0;
}

/*******************************************************************
 简要描述：确保完整接收数据
 参数1：socket描述符
 参数2：数组指针
 参数3：数组长度
 返回值：成功返回0 失败返回-1
********************************************************************/
int recv_all(int sockfd, void *buf, size_t len) 
{
    size_t total_received = 0;
    char *ptr = (char *)buf;
    
    while (total_received < len) 
    {
        ssize_t received = recv(sockfd, ptr + total_received, len - total_received, 0);
        if (received <= 0) 
        {
            return -1; // 接收错误
        }
        total_received += received;
    }
    
    return 0;
}

/*******************************************************************
 简要描述：发送文件
 参数1：socket描述符
 参数2：文件路径
 返回值：成功返回0 失败返回-1
********************************************************************/
int send_file(int sockfd, const char *filepath) 
{
    FILE *file = NULL;
    int ret = EXIT_FAILURE;

    file = fopen(filepath, "rb");
    if (!file) 
    {
        fprintf(stderr, "fopen error for %s: %s\n", filepath, strerror(errno));
        goto cleanup;
    }

    //获取文件大小
    struct stat st;
    if (stat(filepath, &st) != 0)  //获取文件状态并将其存在结构体st中
    {
        fprintf(stderr, "stat error for %s: %s\n", filepath, strerror(errno));
        goto cleanup;
    }
    
    uint64_t file_size = st.st_size;
    printf("File size: %lu bytes\n", file_size);

    // 提取文件名
    char filename[MAX_FILENAME_LEN];
    strncpy(filename, basename(filepath), sizeof(filename)-1);
    filename[sizeof(filename)-1] = '\0';

    printf("%s sending.....\n", filename);
    
    // 发送文件名长度和文件名
    uint32_t name_len = strlen(filename) + 1; // 包含终止符
    uint32_t net_name_len = htonl(name_len);
    if (send_all(sockfd, &net_name_len, sizeof(net_name_len)) < 0) 
    {
        perror("send filename length error");
        goto cleanup;
    }
    
    if (send_all(sockfd, filename, name_len) < 0) 
    {
        perror("send filename error");
        goto cleanup;
    }
    
    // 发送文件大小（转换为网络字节序）
    uint64_t net_file_size = htobe64(file_size);
    if (send_all(sockfd, &net_file_size, sizeof(net_file_size)) < 0) 
    {
        perror("send file size error");
        goto cleanup;
    }
    
    // 发送文件内容
    char sendbuff[BUFFER_SIZE];
    size_t readlen;
    uint64_t total_sent = 0;
    
    while ((readlen = fread(sendbuff, 1, sizeof(sendbuff), file)) > 0) 
    {
        if (send_all(sockfd, sendbuff, readlen) < 0) 
        {
            perror("send file content error");
            goto cleanup;
        }
        total_sent += readlen;
        
        // 显示进度
        printf("Sent: %lu/%lu bytes (%.2f%%)\r", 
               total_sent, file_size, (double)total_sent/file_size*100);
        fflush(stdout);
    }
    
    printf("\n%s sent successfully!\n", filename);
    ret = EXIT_SUCCESS; // 成功

cleanup:
    if (file) 
    {
        fclose(file);
    }
    return ret;
}

/*******************************************************************
 简要描述：接收文件
 参数1：socket描述符
 参数2：保存路径
 返回值：成功返回0 失败返回-1
********************************************************************/
int recv_file(int sockfd, const char *save_dir) 
{
    FILE *file = NULL;
    int ret = EXIT_FAILURE;
    char filename[MAX_SAVE_DIR_LEN] = {0};	
    
    // 接收文件名长度
    uint32_t net_name_len;
    if (recv_all(sockfd, &net_name_len, sizeof(net_name_len)) < 0) 
    {
        perror("recv filename length error");
        goto cleanup;
    }
    
    //判断文件名长度是否越界
    uint32_t name_len = ntohl(net_name_len);
    if (name_len >= sizeof(filename)) 
    {
        fprintf(stderr, "Filename too long: %u\n", name_len);
        goto cleanup;
    }
    
    // 接收文件名
    if (recv_all(sockfd, filename, name_len) < 0) 
    {
        perror("recv filename error");
        goto cleanup;
    }
    
    printf("\n%s receiving.....\n", filename);
    
    // 接收文件大小
    uint64_t net_file_size;
    if (recv_all(sockfd, &net_file_size, sizeof(net_file_size)) < 0) 
    {
        perror("recv file size error");
        goto cleanup;
    }
    
    uint64_t file_size = be64toh(net_file_size); // 转换字节序   
    printf("File size: %lu bytes\n", file_size);
    
    if (file_size > MAX_FILE_SIZE) // 检查文件大小是否合理
    { 
        fprintf(stderr, "File size too large: %lu bytes\n", file_size);
        goto cleanup;
    }
    
    // 构建保存路径
    char filepath[MAX_FILEPATH_LEN] = {0};
    // 确保保存目录不以斜杠结尾
    char clean_save_dir[MAX_SAVE_DIR_LEN];
    strncpy(clean_save_dir, save_dir, sizeof(clean_save_dir)-1);
    clean_save_dir[sizeof(clean_save_dir)-1] = '\0';
    
    // 移除末尾的斜杠
    size_t len = strlen(clean_save_dir);
    if (len > 0 && clean_save_dir[len-1] == '/') 
    {
        clean_save_dir[len-1] = '\0';
    }
    
    snprintf(filepath, sizeof(filepath), "%s/%s", clean_save_dir, filename);
    printf("Saving to: %s\n", filepath);
    
    // 确保目录存在
    char *dir_path = strdup(filepath);
    char *dir_name = dirname(dir_path);
    struct stat st = {0};
    if (stat(dir_name, &st) == -1) 
    {
        mkdir(dir_name, 0700);
    }
    free(dir_path);
    
    // 打开文件
    file = fopen(filepath, "wb");
    if (!file) 
    {
        fprintf(stderr, "fopen error for %s: %s\n", filepath, strerror(errno));
        goto cleanup;
    }
    
    // 接收文件内容
    char recvbuff[BUFFER_SIZE];
    uint64_t total_received = 0;
    
    while (total_received < file_size) 
    {
        size_t remaining = file_size - total_received;
        size_t to_receive = remaining > sizeof(recvbuff) ? sizeof(recvbuff) : remaining;
        
        ssize_t recvlen = recv(sockfd, recvbuff, to_receive, 0);
        if (recvlen <= 0) 
        {
            perror("recv file content error");
            goto cleanup;
        }
        
        total_received += recvlen;
        if (fwrite(recvbuff, 1, recvlen, file) != recvlen) 
        {
            perror("fwrite error");
            goto cleanup;
        }
        
        // 显示进度
        printf("Received: %lu/%lu bytes (%.2f%%)\r", 
               total_received, file_size, (double)total_received/file_size*100);
        fflush(stdout);
    }
    
    printf("\n%s received successfully!\n", filename);
    ret = EXIT_SUCCESS; 

cleanup:
    if (file) 
    {
        fclose(file);
    }
    return ret;
}

/*******************************************************************
 简要描述：线程函数
 返回值：无
********************************************************************/
void* file_transfer_thread(void *arg) 
{
    thread_arg_t *thread_arg = (thread_arg_t*)arg;
    int result;

    if (thread_arg->mIs_send) 
    {
        result = send_file(thread_arg->msockfd, thread_arg->mfilepath);
    } 
    else 
    {
        result = recv_file(thread_arg->msockfd, thread_arg->msave_dir);
    }
    
    if (result != 0) 
    {
        fprintf(stderr, "File transfer failed in thread\n");
    }

}